#include "xSocket.h"
#include <fcntl.h>
#include "xLog.h"
#include <string.h>
namespace xNet
{
    xSocket::xSocket() 
                    : m_dwFd(-1)
                    , m_state(SOCKET_STATE_NOT_INIT)
    {
    }
    xSocket::xSocket(std::string strIp,uint16 wPort)
                    : m_strIp(strIp)
                    , m_wPort(wPort)
    {
    }
    xSocket::~xSocket() 
    {
        this->Close();
    }
    bool xSocket::Init()
    {
        m_dwFd = socket(AF_INET,SOCK_STREAM,0);
        if(m_dwFd == -1)
        {
            return false;
        }
        SetState(SOCKET_STATE_INITED);
        return true;
    }
    void xSocket::Close()
    {
        if(m_dwFd == -1)
        {
            return ;
        }
        //XLOG_INFO("xSocket Close,fd:[%d]",m_dwFd);
        SetState(SOCKET_STATE_CLOSE);
        close(m_dwFd);
        m_dwFd = -1;
    }
    void xSocket::SetNonBlock()
    {
        if(m_dwFd == -1)
        {
            return ;
        }
        int oldFlag = fcntl(m_dwFd,F_GETFL);
        oldFlag |= O_NONBLOCK;
        fcntl(m_dwFd,F_SETFL,oldFlag);
    }

    bool xSocket::AddEPOLL(int epfd,void* ptr)
    {
        if(m_dwFd == -1)
        {
            return false;
        }
        SetNonBlock();
        epoll_event event;
        event.data.fd = m_dwFd;
        event.data.ptr = ptr;
        event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
        return epoll_ctl(epfd,EPOLL_CTL_ADD,m_dwFd,&event) == 0;
    }
    bool xSocket::DelEPOLL(int epfd)
    {
        if(m_dwFd == -1)
        {
            return false;
        }
        return epoll_ctl(epfd,EPOLL_CTL_DEL,m_dwFd,0) == 0;
    }
    bool xSocket::ModEPOLL(int epfd,int ev)
    {
        if(m_dwFd == -1)
        {
            return false;
        }
		epoll_event event;
		event.data.fd = m_dwFd;
		event.events = EPOLLET | EPOLLRDHUP | ev;
		return epoll_ctl(epfd,EPOLL_CTL_MOD,m_dwFd,&event) == 0;
    }

    bool xSocket::SendData(const void* data,int length)
    {
        int ret = 0;
        int offset = 0;
        while (length) 
        { 
            ret = send(m_dwFd, (uint8_t *)data + offset, length, 0);
            if (ret == 0) 
            {
                if (errno != EAGAIN) 
                {
                    return false;
                }
            }
            else if (ret == -1) 
            {
                if (errno != EAGAIN) 
                {
                    return false;
                }
                else 
                {
                    ret = 0;
                }
            }
            length -= ret;
            offset += ret;
        }
        return true;
    }
    bool xSocket::RecvData(void* data,int length)
    {
        int ret = 0;
        int offset = 0;
        while (length) 
        { 
            ret = recv(m_dwFd, (uint8_t *)data + offset, length, 0);
            if (ret == 0) 
            {
                if (errno != EAGAIN) 
                {
                    return false;
                }
            }
            else if (ret == -1) 
            {
                if (errno != EAGAIN) 
                {
                    return false;
                }
                else 
                {
                    ret = 0;
                }
            }
            length -= ret;
            offset += ret;
        }
        return true;
    }

    uint32 xSocket::RecvPacket(void* data)
    {
        uint32 packet_size = 0;
        if(!RecvData((void*)&packet_size,sizeof(uint32)))
        {
            return 0;
        }
        memcpy(data,(void*)&packet_size,sizeof(uint32));
        if(!RecvData((uint8 *)data+sizeof(uint32),packet_size-sizeof(uint32)))
        {
            return 0;
        }
        return packet_size;
    }
    xServiceSocket::xServiceSocket(std::string strIp,uint16 wPort)
                    : xSocket{strIp,wPort}
    {
    }
    bool xServiceSocket::Bind()
    {
        struct sockaddr_in serviceAddr;
        bzero(&serviceAddr,sizeof(serviceAddr));
        socklen_t serviceAddrLen = sizeof(serviceAddr);
        serviceAddr.sin_family = AF_INET;
        serviceAddr.sin_port = htons(m_wPort);
        serviceAddr.sin_addr.s_addr = htonl(INADDR_ANY);//ip
        if(bind(m_dwFd,(struct sockaddr *)&serviceAddr,serviceAddrLen) == -1)
        {
            XLOG_ERROR("Bind Fail,Please check bind port");
            return false;
        }
        return true;
    }
    bool xServiceSocket::Listen()
    {
        if(listen(m_dwFd,128) == -1)
        {
            XLOG_ERROR("Listen Fail,Please check listen api return");
            return false;
        }
        return true;
    }
    xSocket* xServiceSocket::Accept()
    {
        xSocket* newSocket = new xSocket();
        struct sockaddr_in romoteAddr;
        socklen_t addrLen = sizeof(romoteAddr);
        
        bzero(&romoteAddr,sizeof(romoteAddr));
        int dwFd = accept(m_dwFd,(struct sockaddr *)&romoteAddr,&addrLen);
        if(dwFd == -1)
        {
            DELETE(newSocket);
            return nullptr;
        }
        newSocket->SetFd(dwFd);
        XLOG_INFO("Accept new client fd : %d connect success",newSocket->GetFd());
        return newSocket;
    }

    bool xClientSocket::Connect()
    {
        struct sockaddr_in serviceAddr;
        memset(&serviceAddr,0,sizeof(serviceAddr));
        serviceAddr.sin_family = AF_INET;
        serviceAddr.sin_port = htons(m_wPort);
        serviceAddr.sin_addr.s_addr = inet_addr(m_strIp.c_str());
        if(connect(m_dwFd,(struct sockaddr *)&serviceAddr,sizeof(serviceAddr)) == -1)
        {
            XLOG_ERROR("Connect server fail,please check server romote ip and port");
            SetState(SOCKET_STATE_DISCONNECTED);
            return false;
        }
        SetState(SOCKET_STATE_CONNECTED);
        return true;
    }
    bool xClientSocket::TryConnect()
    {
        if(m_state == SOCKET_STATE_CONNECTED)
        {
            return true;
        }
        if(m_dwFd == -1 || m_state == SOCKET_STATE_NOT_INIT)
        {
            if(!Init())
            {
                return false;
            }
        }
        return Connect();
    }
    void xClientSocket::DisConnect()
    {
        Close();
    }
}